home *** CD-ROM | disk | FTP | other *** search
/ OpenGL Superbible (2nd Edition) / OpenGL SuperBible e2.iso / tools / GLUT-3.7 / LIB / GLUT / glut_gamemode.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-08-12  |  16.0 KB  |  678 lines

  1.  
  2. /* Copyright (c) Mark J. Kilgard, 1998. */
  3.  
  4. /* This program is freely distributable without licensing fees 
  5.    and is provided without guarantee or warrantee expressed or 
  6.    implied. This program is -not- in the public domain. */
  7.  
  8. #include <assert.h>
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12.  
  13. #ifdef _WIN32
  14. #include <windows.h>
  15. #endif
  16.  
  17. #ifndef _WIN32
  18. #include <X11/Xlib.h>
  19. #include <X11/Xatom.h>
  20.  
  21. /* SGI optimization introduced in IRIX 6.3 to avoid X server
  22.    round trips for interning common X atoms. */
  23. #if defined(_SGI_EXTRA_PREDEFINES) && !defined(NO_FAST_ATOMS)
  24. #include <X11/SGIFastAtom.h>
  25. #else
  26. #define XSGIFastInternAtom(dpy,string,fast_name,how) XInternAtom(dpy,string,how)
  27. #endif
  28. #endif  /* not _WIN32 */
  29.  
  30. #include "glutint.h"
  31.  
  32. int __glutDisplaySettingsChanged = 0;
  33. static DisplayMode *dmodes, *currentDm = NULL;
  34. static int ndmodes = -1;
  35. GLUTwindow *__glutGameModeWindow = NULL;
  36.     
  37. #ifdef TEST
  38. static char *compstr[] =
  39. {
  40.   "none", "=", "!=", "<=", ">=", ">", "<", "~"
  41. };
  42. static char *capstr[] =
  43. {
  44.   "width", "height", "bpp", "hertz", "num"
  45. };
  46. #endif
  47.  
  48. void
  49. __glutCloseDownGameMode(void)
  50. {
  51.   if (__glutDisplaySettingsChanged) {
  52. #ifdef _WIN32
  53.     /* Assumes that display settings have been changed, that
  54.        is __glutDisplaySettingsChanged is true. */
  55.     ChangeDisplaySettings(NULL, 0);
  56. #endif
  57.     __glutDisplaySettingsChanged = 0;
  58.   }
  59.   __glutGameModeWindow = NULL;
  60. }
  61.  
  62. void APIENTRY
  63. glutLeaveGameMode(void)
  64. {
  65.   if (__glutGameModeWindow == NULL) {
  66.     __glutWarning("not in game mode so cannot leave game mode");
  67.     return;
  68.   }
  69.   __glutDestroyWindow(__glutGameModeWindow,
  70.     __glutGameModeWindow);
  71.   __glutGameModeWindow = NULL;
  72. }
  73.  
  74. #ifdef _WIN32
  75.  
  76. /* Same values as from MSDN's SetDisp.c example. */
  77. #define MIN_WIDTH 400
  78. #define MIN_FREQUENCY 60
  79.  
  80. static void
  81. initGameModeSupport(void)
  82. {
  83.   DEVMODE dm;
  84.   DWORD mode;
  85.   int i;
  86.  
  87.   if (ndmodes >= 0) {
  88.     /* ndmodes is initially -1 to indicate no
  89.        dmodes allocated yet. */
  90.     return;
  91.   }
  92.  
  93.   /* Determine how many display modes there are. */
  94.   ndmodes = 0;
  95.   mode = 0;
  96.   while (EnumDisplaySettings(NULL, mode, &dm)) {
  97.     if (dm.dmPelsWidth >= MIN_WIDTH &&
  98.       (dm.dmDisplayFrequency == 0 ||
  99.       dm.dmDisplayFrequency >= MIN_FREQUENCY)) {
  100.       ndmodes++;
  101.     }
  102.     mode++;
  103.   }
  104.  
  105.   /* Allocate memory for a list of all the display modes. */
  106.   dmodes = (DisplayMode*)
  107.     malloc(ndmodes * sizeof(DisplayMode));
  108.  
  109.   /* Now that we know how many display modes to expect,
  110.      enumerate them again and save the information in
  111.      the list we allocated above. */
  112.   i = 0;
  113.   mode = 0;
  114.   while (EnumDisplaySettings(NULL, mode, &dm)) {
  115.     /* Try to reject any display settings that seem unplausible. */
  116.     if (dm.dmPelsWidth >= MIN_WIDTH &&
  117.       (dm.dmDisplayFrequency == 0 ||
  118.       dm.dmDisplayFrequency >= MIN_FREQUENCY)) {
  119.       dmodes[i].devmode = dm;
  120.       dmodes[i].valid = 1;  /* XXX Not used for now. */
  121.       dmodes[i].cap[DM_WIDTH] = dm.dmPelsWidth;
  122.       dmodes[i].cap[DM_HEIGHT] = dm.dmPelsHeight;
  123.       dmodes[i].cap[DM_PIXEL_DEPTH] = dm.dmBitsPerPel;
  124.       if (dm.dmDisplayFrequency == 0) {
  125.      /* Guess a reasonable guess. */
  126.     /* Lame Windows 95 version of EnumDisplaySettings. */
  127.         dmodes[i].cap[DM_HERTZ] = 60;
  128.       } else {
  129.     dmodes[i].cap[DM_HERTZ] = dm.dmDisplayFrequency;
  130.       }
  131.       i++;
  132.     }
  133.     mode++;
  134.   }
  135.  
  136.   assert(i == ndmodes);
  137. }
  138.  
  139. #else
  140.  
  141. /* X Windows version of initGameModeSupport. */
  142. static void
  143. initGameModeSupport(void)
  144. {
  145.   if (ndmodes >= 0) {
  146.     /* ndmodes is initially -1 to indicate no
  147.        dmodes allocated yet. */
  148.     return;
  149.   }
  150.  
  151.   /* Determine how many display modes there are. */
  152.   ndmodes = 0;
  153. }
  154.  
  155. #endif
  156.  
  157. /* This routine is based on similiar code in glut_dstr.c */
  158. static DisplayMode *
  159. findMatch(DisplayMode * dmodes, int ndmodes,
  160.   Criterion * criteria, int ncriteria)
  161. {
  162.   DisplayMode *found;
  163.   int *bestScore, *thisScore;
  164.   int i, j, numok, result, worse, better;
  165.  
  166.   found = NULL;
  167.   numok = 1;            /* "num" capability is indexed from 1,
  168.                            not 0. */
  169.  
  170.   /* XXX alloca canidate. */
  171.   bestScore = (int *) malloc(ncriteria * sizeof(int));
  172.   if (!bestScore) {
  173.     __glutFatalError("out of memory.");
  174.   }
  175.   for (j = 0; j < ncriteria; j++) {
  176.     /* Very negative number. */
  177.     bestScore[j] = -32768;
  178.   }
  179.  
  180.   /* XXX alloca canidate. */
  181.   thisScore = (int *) malloc(ncriteria * sizeof(int));
  182.   if (!thisScore) {
  183.     __glutFatalError("out of memory.");
  184.   }
  185.  
  186.   for (i = 0; i < ndmodes; i++) {
  187.     if (dmodes[i].valid) {
  188.       worse = 0;
  189.       better = 0;
  190.  
  191.       for (j = 0; j < ncriteria; j++) {
  192.         int cap, cvalue, dvalue;
  193.  
  194.         cap = criteria[j].capability;
  195.         cvalue = criteria[j].value;
  196.         if (cap == NUM) {
  197.           dvalue = numok;
  198.         } else {
  199.           dvalue = dmodes[i].cap[cap];
  200.         }
  201. #ifdef TEST
  202.         if (verbose)
  203.           printf("  %s %s %d to %d\n",
  204.             capstr[cap], compstr[criteria[j].comparison], cvalue, dvalue);
  205. #endif
  206.         switch (criteria[j].comparison) {
  207.         case EQ:
  208.           result = cvalue == dvalue;
  209.           thisScore[j] = 1;
  210.           break;
  211.         case NEQ:
  212.           result = cvalue != dvalue;
  213.           thisScore[j] = 1;
  214.           break;
  215.         case LT:
  216.           result = dvalue < cvalue;
  217.           thisScore[j] = dvalue - cvalue;
  218.           break;
  219.         case GT:
  220.           result = dvalue > cvalue;
  221.           thisScore[j] = dvalue - cvalue;
  222.           break;
  223.         case LTE:
  224.           result = dvalue <= cvalue;
  225.           thisScore[j] = dvalue - cvalue;
  226.           break;
  227.         case GTE:
  228.           result = (dvalue >= cvalue);
  229.           thisScore[j] = dvalue - cvalue;
  230.           break;
  231.         case MIN:
  232.           result = dvalue >= cvalue;
  233.           thisScore[j] = cvalue - dvalue;
  234.           break;
  235.         }
  236.  
  237. #ifdef TEST
  238.         if (verbose)
  239.           printf("                result=%d   score=%d   bestScore=%d\n", result, thisScore[j], bestScore[j]);
  240. #endif
  241.  
  242.         if (result) {
  243.           if (better || thisScore[j] > bestScore[j]) {
  244.             better = 1;
  245.           } else if (thisScore[j] == bestScore[j]) {
  246.             /* Keep looking. */
  247.           } else {
  248.             goto nextDM;
  249.           }
  250.         } else {
  251.           if (cap == NUM) {
  252.             worse = 1;
  253.           } else {
  254.             goto nextDM;
  255.           }
  256.         }
  257.  
  258.       }
  259.  
  260.       if (better && !worse) {
  261.         found = &dmodes[i];
  262.         for (j = 0; j < ncriteria; j++) {
  263.           bestScore[j] = thisScore[j];
  264.         }
  265.       }
  266.       numok++;
  267.  
  268.     nextDM:;
  269.  
  270.     }
  271.   }
  272.   free(bestScore);
  273.   free(thisScore);
  274.   return found;
  275. }
  276.  
  277. /**
  278.  * Parses strings in the form of:
  279.  *  800x600
  280.  *  800x600:16
  281.  *  800x600@60
  282.  *  800x600:16@60
  283.  *  @60
  284.  *  :16
  285.  *  :16@60
  286.  * NOTE that @ before : is not parsed.
  287.  */
  288. static int
  289. specialCaseParse(char *word, Criterion * criterion, int mask)
  290. {
  291.   char *xstr, *response;
  292.   int got;
  293.   int width, height, bpp, hertz;
  294.  
  295.   switch(word[0]) {
  296.   case '0':
  297.   case '1':
  298.   case '2':
  299.   case '3':
  300.   case '4':
  301.   case '5':
  302.   case '6':
  303.   case '7':
  304.   case '8':
  305.   case '9':
  306.     /* The WWWxHHH case. */
  307.     if (mask & (1 << DM_WIDTH)) {
  308.       return -1;
  309.     }
  310.     xstr = strpbrk(&word[1], "x");
  311.     if (xstr) {
  312.       width = (int) strtol(word, &response, 0);
  313.       if (response == word || response[0] != 'x') {
  314.         /* Not a valid number OR needs to be followed by 'x'. */
  315.     return -1;
  316.       }
  317.       height = (int) strtol(&xstr[1], &response, 0);
  318.       if (response == &xstr[1]) {
  319.         /* Not a valid number. */
  320.     return -1;
  321.       }
  322.       criterion[0].capability = DM_WIDTH;
  323.       criterion[0].comparison = EQ;
  324.       criterion[0].value = width;
  325.       criterion[1].capability = DM_HEIGHT;
  326.       criterion[1].comparison = EQ;
  327.       criterion[1].value = height;
  328.       got = specialCaseParse(response,
  329.         &criterion[2], 1 << DM_WIDTH);
  330.       if (got >= 0) {
  331.         return got + 2;
  332.       } else {
  333.         return -1;
  334.       }
  335.     }    
  336.     return -1;
  337.   case ':':
  338.     /* The :BPP case. */
  339.     if (mask & (1 << DM_PIXEL_DEPTH)) {
  340.       return -1;
  341.     }
  342.     bpp = (int) strtol(&word[1], &response, 0);
  343.     if (response == &word[1]) {
  344.       /* Not a valid number. */
  345.       return -1;
  346.     }
  347.     criterion[0].capability = DM_PIXEL_DEPTH;
  348.     criterion[0].comparison = EQ;
  349.     criterion[0].value = bpp;
  350.     got = specialCaseParse(response,
  351.       &criterion[1], 1 << DM_WIDTH | 1 << DM_PIXEL_DEPTH);
  352.     if (got >= 0) {
  353.       return got + 1;
  354.     } else {
  355.       return -1;
  356.     }
  357.   case '@':
  358.     /* The @HZ case. */
  359.     if (mask & (1 << DM_HERTZ)) {
  360.       return -1;
  361.     }
  362.     hertz = (int) strtol(&word[1], &response, 0);
  363.     if (response == &word[1]) {
  364.       /* Not a valid number. */
  365.       return -1;
  366.     }
  367.     criterion[0].capability = DM_HERTZ;
  368.     criterion[0].comparison = EQ;
  369.     criterion[0].value = hertz;
  370.     got = specialCaseParse(response,
  371.       &criterion[1], ~DM_HERTZ);
  372.     if (got >= 0) {
  373.       return got + 1;
  374.     } else {
  375.       return -1;
  376.     }
  377.   case '\0':
  378.     return 0;
  379.   }
  380.   return -1;
  381. }
  382.  
  383. /* This routine is based on similiar code in glut_dstr.c */
  384. static int
  385. parseCriteria(char *word, Criterion * criterion)
  386. {
  387.   char *cstr, *vstr, *response;
  388.   int comparator, value;
  389.  
  390.   cstr = strpbrk(word, "=><!~");
  391.   if (cstr) {
  392.     switch (cstr[0]) {
  393.     case '=':
  394.       comparator = EQ;
  395.       vstr = &cstr[1];
  396.       break;
  397.     case '~':
  398.       comparator = MIN;
  399.       vstr = &cstr[1];
  400.       break;
  401.     case '>':
  402.       if (cstr[1] == '=') {
  403.         comparator = GTE;
  404.         vstr = &cstr[2];
  405.       } else {
  406.         comparator = GT;
  407.         vstr = &cstr[1];
  408.       }
  409.       break;
  410.     case '<':
  411.       if (cstr[1] == '=') {
  412.         comparator = LTE;
  413.         vstr = &cstr[2];
  414.       } else {
  415.         comparator = LT;
  416.         vstr = &cstr[1];
  417.       }
  418.       break;
  419.     case '!':
  420.       if (cstr[1] == '=') {
  421.         comparator = NEQ;
  422.         vstr = &cstr[2];
  423.       } else {
  424.         return -1;
  425.       }
  426.       break;
  427.     default:
  428.       return -1;
  429.     }
  430.     value = (int) strtol(vstr, &response, 0);
  431.     if (response == vstr) {
  432.       /* Not a valid number. */
  433.       return -1;
  434.     }
  435.     *cstr = '\0';
  436.   } else {
  437.     comparator = NONE;
  438.   }
  439.   switch (word[0]) {
  440.   case 'b':
  441.     if (!strcmp(word, "bpp")) {
  442.       criterion[0].capability = DM_PIXEL_DEPTH;
  443.       if (comparator == NONE) {
  444.         return -1;
  445.       } else {
  446.         criterion[0].comparison = comparator;
  447.         criterion[0].value = value;
  448.         return 1;
  449.       }
  450.     }
  451.     return -1;
  452.   case 'h':
  453.     if (!strcmp(word, "height")) {
  454.       criterion[0].capability = DM_HEIGHT;
  455.       if (comparator == NONE) {
  456.         return -1;
  457.       } else {
  458.         criterion[0].comparison = comparator;
  459.         criterion[0].value = value;
  460.         return 1;
  461.       }
  462.     }
  463.     if (!strcmp(word, "hertz")) {
  464.       criterion[0].capability = DM_HERTZ;
  465.       if (comparator == NONE) {
  466.         return -1;
  467.       } else {
  468.         criterion[0].comparison = comparator;
  469.         criterion[0].value = value;
  470.         return 1;
  471.       }
  472.     }
  473.     return -1;
  474.   case 'n':
  475.     if (!strcmp(word, "num")) {
  476.       criterion[0].capability = DM_NUM;
  477.       if (comparator == NONE) {
  478.         return -1;
  479.       } else {
  480.         criterion[0].comparison = comparator;
  481.         criterion[0].value = value;
  482.         return 1;
  483.       }
  484.     }
  485.     return -1;
  486.   case 'w':
  487.     if (!strcmp(word, "width")) {
  488.       criterion[0].capability = DM_WIDTH;
  489.       if (comparator == NONE) {
  490.         return -1;
  491.       } else {
  492.         criterion[0].comparison = comparator;
  493.         criterion[0].value = value;
  494.         return 1;
  495.       }
  496.     }
  497.     return -1;
  498.   }
  499.   if (comparator == NONE) {
  500.     return specialCaseParse(word, criterion, 0);
  501.   }
  502.   return -1;
  503. }
  504.  
  505. /* This routine is based on similiar code in glut_dstr.c */
  506. static Criterion *
  507. parseDisplayString(const char *display, int *ncriteria)
  508. {
  509.   Criterion *criteria = NULL;
  510.   int n, parsed;
  511.   char *copy, *word;
  512.  
  513.   copy = __glutStrdup(display);
  514.   /* Attempt to estimate how many criteria entries should be
  515.      needed. */
  516.   n = 0;
  517.   word = strtok(copy, " \t");
  518.   while (word) {
  519.     n++;
  520.     word = strtok(NULL, " \t");
  521.   }
  522.   /* Allocate number of words of criteria.  A word
  523.      could contain as many as four criteria in the
  524.      worst case.  Example: 800x600:16@60 */
  525.   criteria = (Criterion *) malloc(4 * n * sizeof(Criterion));
  526.   if (!criteria) {
  527.     __glutFatalError("out of memory.");
  528.   }
  529.  
  530.   /* Re-copy the copy of the display string. */
  531.   strcpy(copy, display);
  532.  
  533.   n = 0;
  534.   word = strtok(copy, " \t");
  535.   while (word) {
  536.     parsed = parseCriteria(word, &criteria[n]);
  537.     if (parsed >= 0) {
  538.       n += parsed;
  539.     } else {
  540.       __glutWarning("Unrecognized game mode string word: %s (ignoring)\n", word);
  541.     }
  542.     word = strtok(NULL, " \t");
  543.   }
  544.  
  545.   free(copy);
  546.   *ncriteria = n;
  547.   return criteria;
  548. }
  549.  
  550. void APIENTRY
  551. glutGameModeString(const char *string)
  552. {
  553.   Criterion *criteria;
  554.   int ncriteria;
  555.  
  556.   initGameModeSupport();
  557.   criteria = parseDisplayString(string, &ncriteria);
  558.   currentDm = findMatch(dmodes, ndmodes, criteria, ncriteria);
  559.   free(criteria);
  560. }
  561.  
  562. int APIENTRY
  563. glutEnterGameMode(void)
  564. {
  565.   GLUTwindow *window;
  566.   int width, height;
  567.   Window win;
  568.  
  569.   if (__glutMappedMenu) {
  570.     __glutFatalUsage("entering game mode not allowed while menus in use");
  571.   }
  572.   if (__glutGameModeWindow) {
  573.     /* Already in game mode, so blow away game mode
  574.        window so apps can change resolutions. */
  575.     window = __glutGameModeWindow;
  576.     /* Setting the game mode window to NULL tricks
  577.        the window destroy code into not undoing the
  578.        screen display change since we plan on immediately
  579.        doing another mode change. */
  580.     __glutGameModeWindow = NULL;
  581.     __glutDestroyWindow(window, window);
  582.   }
  583.  
  584.   /* Assume default screen size until we find out if we
  585.      can actually change the display settings. */
  586.   width = __glutScreenWidth;
  587.   height = __glutScreenHeight;
  588.  
  589.   if (currentDm) {
  590. #ifdef _WIN32
  591.     LONG status;
  592.     static int registered = 0;
  593.  
  594.     status = ChangeDisplaySettings(¤tDm->devmode,
  595.       CDS_FULLSCREEN);
  596.     if (status == DISP_CHANGE_SUCCESSFUL) {
  597.       __glutDisplaySettingsChanged = 1;
  598.       width = currentDm->cap[DM_WIDTH];
  599.       height = currentDm->cap[DM_HEIGHT];
  600.       if (!registered) {
  601.         atexit(__glutCloseDownGameMode);
  602.         registered = 1;
  603.       }
  604.     } else {
  605.       /* Switch back to default resolution. */
  606.       ChangeDisplaySettings(NULL, 0);
  607.     }
  608. #endif
  609.   }
  610.  
  611.   window = __glutCreateWindow(NULL, 0, 0,
  612.     width, height, /* game mode */ 1);
  613.   win = window->win;
  614.  
  615. #if !defined(_WIN32)
  616.   if (__glutMotifHints == None) {
  617.     __glutMotifHints = XSGIFastInternAtom(__glutDisplay, "_MOTIF_WM_HINTS",
  618.       SGI_XA__MOTIF_WM_HINTS, 0);
  619.     if (__glutMotifHints == None) {
  620.       __glutWarning("Could not intern X atom for _MOTIF_WM_HINTS.");
  621.     }
  622.   }
  623.  
  624.   /* Game mode window is a toplevel window. */
  625.   XSetWMProtocols(__glutDisplay, win, &__glutWMDeleteWindow, 1);
  626. #endif
  627.  
  628.   /* Schedule the fullscreen property to be added and to
  629.      make sure the window is configured right.  Win32
  630.      doesn't need this. */
  631.   window->desiredX = 0;
  632.   window->desiredY = 0;
  633.   window->desiredWidth = width;
  634.   window->desiredHeight = height;
  635.   window->desiredConfMask |= CWX | CWY | CWWidth | CWHeight;
  636. #ifdef _WIN32
  637.   /* Win32 does not want to use GLUT_FULL_SCREEN_WORK
  638.      for game mode because we need to be maximizing
  639.      the window in game mode, not just sizing it to
  640.      take up the full screen.  The Win32-ness of game
  641.      mode happens when you pass 1 in the gameMode parameter
  642.      to __glutCreateWindow above.  A gameMode of creates
  643.      a WS_POPUP window, not a standard WS_OVERLAPPEDWINDOW
  644.      window.  WS_POPUP ensures the taskbar is hidden. */
  645.   __glutPutOnWorkList(window,
  646.     GLUT_CONFIGURE_WORK);
  647. #else
  648.   __glutPutOnWorkList(window,
  649.     GLUT_CONFIGURE_WORK | GLUT_FULL_SCREEN_WORK);
  650. #endif
  651.  
  652.   __glutGameModeWindow = window;
  653.   return window->num + 1;
  654. }
  655.  
  656. int APIENTRY
  657. glutGameModeGet(GLenum mode)
  658. {
  659.   switch (mode) {
  660.   case GLUT_GAME_MODE_ACTIVE:
  661.     return __glutGameModeWindow != NULL;
  662.   case GLUT_GAME_MODE_POSSIBLE:
  663.     return currentDm != NULL;
  664.   case GLUT_GAME_MODE_WIDTH:
  665.     return currentDm ? currentDm->cap[DM_WIDTH] : -1;
  666.   case GLUT_GAME_MODE_HEIGHT:
  667.     return currentDm ? currentDm->cap[DM_HEIGHT] : -1;
  668.   case GLUT_GAME_MODE_PIXEL_DEPTH:
  669.     return currentDm ? currentDm->cap[DM_PIXEL_DEPTH] : -1;
  670.   case GLUT_GAME_MODE_REFRESH_RATE:
  671.     return currentDm ? currentDm->cap[DM_HERTZ] : -1;
  672.   case GLUT_GAME_MODE_DISPLAY_CHANGED:
  673.     return __glutDisplaySettingsChanged;
  674.   default:
  675.     return -1;
  676.   }
  677. }
  678.